刚刚开始学习,还处于菜鸟阶段,请大家不吝赐教,谢谢。
一、基本概念
1.进程与线程
进程作为程序的一次执行,它是有独立的内存空间的;而线程可以理解为一个进程中的多个独立运行的子任务,是CPU调度的最小单元。一个进程可以包含多个线程。
2.实现线程的两种方式
(1)继承Thread类
(2)实现Runnable接口(推荐, 因为可以支持多继承)
注意:线程是一个子任务,CPU以随机的时间来调用他的run方法,若同时启动多个线程,调用start()方法的顺序并不能代表线程的启动顺序。
3.
3.1线程工作的大致原理
有一个主内存,每一个线程都维护一个自己的工作内存,当线程操作变量的时候会在自己的工作内存中建立一个对变量的拷贝,在自己的内存中操作完之后再将变量值写入到主内存中
3.1 什么是线程安全
多个线程同时访问同一代码,不会产生不确定的后果。所以我们可以用锁来控制多个线程访问共享资源的方式。
3.2 为什么会出现线程安全问题
举一个简单的例子:
i–,这条指令在某些虚拟机中被分为以下3步来执行
(1)取得原值i
(2)计算i-1
(3)对i进行赋值
在这3个步骤中,如果有多个线程同时访问,一个线程刚执行到第二步,即被剥夺了执行权,另一线程过来从第一部开始执行,就会出现线程安全问题。
4.线程的状态(5种)
新建, 就绪,运行,阻塞,终止
新建:刚刚new Thread()
就绪:调用了start()方法,但是还没有获取到CPU的执行权
运行:获取到了CPU的执行权,开始执行run方法
阻塞:调用了sleep或者wait或者正在进行io操作
终止:run方法执行完毕;调用了stop方法;被中断
5.停止线程方法
调用interrupt()方法,但是不会立即停止线程,而是给线程增加一个标记,在后面判断这个标记并抛出InterrupeedException异常。interrupt()方法不能中断正在运行的线程,只能中断正在阻塞过程中的线程。
isInterruped()方法与interruped()方法的区别?
isInterruped()用来测试当前线程是否已经是中断状态
interruped()用来测试当前线程是否已经是中断状态,并且清除该线程的中断状态
6.线程间的协作(wait/notify/sleep/yield/join)
感觉这位大神的总结很全面经典。
请参考:http://blog.csdn.net/ikduoluo9/article/details/78250306
7.线程的优先级
优先级具有继承性,比如A线程启动B线程,则A,B的优先级是一样的。
8.synchronized对象的用法
(1)修饰代码块,其称为同步代码块,作用范围是被大括号括起来的部分,作用的对象是调用这个代码块的对象
示例代码1:
public class ThreadTest {
public static void main(String[] args) {
Thread1 t = new Thread1();
//锁的作用对象为t,其他试图访问该对象的线程均被阻塞
new Thread(t).start();
new Thread(t).start();
}
}
class Thread1 implements Runnable
{
private static int count =0;
@Override
public void run()
{
synchronized (this) {
for (int i = 0 ; i < 6; i++)
{
System.out.println(Thread.currentThread().getName() + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
输出:
Thread-00
Thread-01
Thread-02
Thread-03
Thread-04
Thread-05
Thread-16
Thread-17
Thread-18
Thread-19
Thread-110
Thread-111
当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
实例代码2:
package thread;
public class ThreadTest {
public static void main(String[] args) {
//Thread1 t = new Thread1();
new Thread(new Thread1()).start();
new Thread(new Thread1()).start();
}
}
class Thread1 implements Runnable
{
private static int count =0;
@Override
public void run()
{
synchronized (this) {
for (int i = 0 ; i <6; i++)
{
System.out.println(Thread.currentThread().getName() + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
输出:
Thread-00
Thread-11
Thread-12
Thread-03
Thread-14
Thread-05
Thread-06
Thread-17
Thread-08
Thread-19
Thread-010
Thread-111
这个因为syncronized是作用于对象上的,而new Thread1()相当于是new了两个对象,每个对象都有自己的锁,他们之间的执行互不干扰。
当一个线程正在访问同步代码块的时候,另一个线程是可以访问非同步代码块的。
可以给只定的对象加锁,这样的话只有一个线程访问完这个对象其他的线程才可以访问。
需要注意是的是:这个制定的对象必须是多个线程之间可以共享的对象,不然是没有意义的。
(2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
(3)修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
我们可以看到输出,是因为静态方法是属于类的,其作用域是这个类的所有对象,因为new的两个线程使用同一把锁。
示例代码:
package thread;
public class ThreadTest {
public static void main(String[] args) {
//Thread1 t = new Thread1();
new Thread(new Thread1()).start();
new Thread(new Thread1()).start();
}
}
class Thread1 implements Runnable
{
private static int count =0;
@Override
public void run()
{
test();
}
public synchronized static void test()
{
for (int i = 0 ; i <6; i++)
{
System.out.println(Thread.currentThread().getName() + count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出:
Thread-00
Thread-01
Thread-02
Thread-03
Thread-04
Thread-05
Thread-16
Thread-17
Thread-18
Thread-19
Thread-110
Thread-111
(4)修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
使用synchronized的注意事项
(1)synchronized关键字不能继承。
(2)定义接口不能使用synchronized关键字
(3)构造方法中不可以使用,但可以使用同步块
简单对synchronized做一下总结:
(1)可修饰的东西:代码块,方法,类,对象
(2)若修饰代码块或者非静态方法或者对象,则是从对象获取锁;若修饰静态方法或者类,则是从类获取锁。
9.关于java中的Lock
lock提供了与syncronized对象相似的功能,但是一定要在使用时手动加锁与释放。
lock与syncronized相比有什么优点与缺点?
缺点:不够便捷
为什么要使用lock?
当我们使用syncronized来修饰方法时,若一个线程获取到了锁并且正在执行的时候,其他的线程只能够干巴巴的等待,如果说当前线程正在执行比较耗时的操作例如等待IO或者在sleep,则会非常的影响效率,所以说我们需要这样的一种锁,能够等待一定的时间就不再等待,或者是能够响应中断,来提高多线程操作的效率。lock提供的锁具有可操作性,例如可中断获取以及可超时获取,另外还提供了可读写锁以及可重入锁这样强大的功能。
Lock与syncronized最大的区别
Lock需要手动释放!
lock接口的相关API如下:
void lock()
获取锁,调用该方法当前线程将会获取锁,当锁获取后,方法返回。获取不到锁,该线程就会一直等待。锁获取到若出现异常是不会释放锁的,因此一般要在try,catch块中使用,在finally中说释放锁。
boolean tryLock()
尝试非阻塞的获取锁,即调用之后立即返回,锁没获取到则返回false,而不会等待。
boolean tryLock(long time,TimeUnit unit) throws InterruptedException
在获取锁时等待一定的时间,以下情况会返回:时间内获取到了锁,时间内被中断,时间到了没有获取到锁。
void lockInterruptibly() throws InterruptedException
可中断获取锁,eg:若A,B线程同时去获取锁,A线程获取到了但是B线程没有获取到,一直在等待,这时可以通过ThreadB.interrupt() 中断线程B的等待过程。
void unlock() 释放锁
概念:
可重入锁:假设一个类中有两个被syncronized修饰的方法method1,method2,若线程A获取到了该对象的锁并且在执行method1,在执行method1的时候还要去调用method2,线程A不必再去重新获取该对象的锁。。。非可重入锁在遇到这种情况的时候会产生死锁。(ReentrantLock与syncronized都是可重入锁)
公平锁: 尽量以请求锁的顺序来赋予锁
* 非公平锁*:反之
在调用ReentrantLock的时候可以指定他是公平锁还是非公平锁,通过传入一个布尔值变量。在绝对时间上,最先发起获取锁请求的线程能够最先获取到锁,即公平锁即每次都是等待时间最久的线程获取到锁。
**读写锁:**ReentrantReadWriteLock,实现读写分离。读与写是互斥的,写与写是互斥的,但是读与读不是互斥的。
参考文章:http://blog.csdn.net/qq_15763035/article/details/51868927
未完待续。。。。。