线程的基本状态及其转换
跟进程的状态差不多,只是调度的单元从进程切换到线程
blocked\waiting\timedwaiting都是阻塞状态
线程类的常用方法
sleep();
精度取决于系统计时器的精度
setPriority();
设置线程优先级,取值范围1-10
建议操作系统哪个线程优先级较高,但是不能100%确定高优先级就先运行或者先运行完成。
//与start类似
Thread t = new Thread();
t.setPriority(Thread.MAX_PRIORITY);
yield();
线程让步
join();
线程插队
setDaemon();
设置后台线程
如果把一个线程设置为后台线程,jvm不会管后台线程,所有的前台线程运行完之后jvm虚拟机直接退出。
线程安全
举个简单的例子
AB两个线程
public class ThreadSynchronizedTest {
public static void main(String[] args) {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
}
}
class ThreadA extends Thread{
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);//此时ThreadA进入中断状态CPU进行其他调度
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程A在执行");
}
}
}
class ThreadB extends Thread{
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程B在执行");
}
}
}
执行结果显而易见,ThreadA和ThreadB交替打印
public class ThreadSynchronizedTest {
public static final Object ANYOBJECT = new Object();//静态的对象,运行时方便调用。
public static void main(String[] args) {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
}
}
class ThreadA extends Thread {
public void run() {
//synchronized关键字获取锁的时候计数器+1,当运行完毕释放锁的时候,计数器-1
synchronized (ThreadSynchronizedTest.ANYOBJECT) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);/此时ThreadA进入中断状态CPU进行其他调度
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程A在执行");
}
}
}
}
class ThreadB extends Thread {
public void run() {
synchronized (ThreadSynchronizedTest.ANYOBJECT) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程B在执行");
}
}
}
}
结果线程A先打印,谁先获取锁,谁先执行。正常情况下A获取锁后执行代码,但是遇到sleep的时候A则进入中断,此时CPU调度应该执行其他线程,但是因为A有锁,此时CPU调度到线程B,线程B无法获取锁,B进入阻塞状态,因为此时锁的状态是1,所以A继续执行
如果我们想要说我们想让一个线程执行的时候另外一个线程不执行,那么只需要让这两个线程,我们只需要对同一个对象进行加锁就行,至于这个对象具体是什么不重要,然后让这两个线程获取该对象的锁即可。获取到的先执行。
另外,如果当前线程已经拥有一个对象的锁,该线程可以再次尝试去获取这个锁,再+1,这是锁的可重录性。,如果一个对象被锁定,再次加锁,会导致死锁的出现类似synchronized嵌套。
所以之前发成绩单的代码可以改成
public class ThreadClassT2 {
public static void main(String[] args) {
DistributeThread2 dt = new DistributeThread2();
Thread t1 = new Thread(dt, "我");
Thread t2 = new Thread(dt, "学生甲");
Thread t3 = new Thread(dt, "学生乙");
t1.start();
t2.start();
t3.start();
}
}
class DistributeThread2 implements Runnable{
//避免出现重复的情况
private Object lock = new Object();
int formCount = 100;
public void run() {
while (true) {//这里为什么要while true,因为如果按照之前的写法,获取锁之后再进入while(formCount > 0),那么第一个进入到这里面的线程将执行完之后再跳出while循环然后再释放锁,其他线程将无法进行while里面的操作
synchronized (lock) {
if (formCount > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在分发学生NO:" + formCount-- + "学生信息表格");
}else {
break;
}
}
}
}
}
如果大家都是同一个锁,那么可以用synchronized (this) ,这个叫synchronized块
我们也可以把synchronized写在方法前面,这样就编程synchronized方法,锁的仍然是this对象,总之还是要同一个锁,至于锁是什么类型,无所谓
如果synchronized修饰的是一个静态方法,那么它锁的对象是“静态方法所在类.class”对象,一旦这个方法获取了锁,那么这个类中所有被synchronized修饰的方法都进不去了。
死锁
上面的代码同样可以用信号量来代替synchronized来实现
public static void main(String[] args) {
ThreadSemaphoreTest dt = new ThreadSemaphoreTest();
Thread t1 = new Thread(dt, "我");
Thread t2 = new Thread(dt, "学生甲");
Thread t3 = new Thread(dt, "学生乙");
t1.start();
t2.start();
t3.start();
}
}
class Signal{
public static final Semaphore LOCK = new Semaphore(1);
}
class ThreadSemaphoreTest implements Runnable{
//避免出现重复的情况
//private Object lock = new Object();
int formCount = 50;
public void run() {
while (true) {
try {
Signal.LOCK.acquire();//用信号量来做
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (formCount > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在分发学生NO:" + formCount-- + "学生信息表格");
} else {
break;
}
Signal.LOCK.release();
}
}
}
ps:synchronized是隐式锁,看不到加锁和解锁的过程,jdk1.5concurrent并发包里面引入了ReentrantLock显示锁,有lock和unlock方法。
另外释放锁一定要放在finally里面。确保如果程序获得锁之后出现异常,也能释放锁给其他线程继续使用。
ReentrantLock中可以设置公平锁,即
private Lock lock = new ReentrantLock(true);//线程轮流执行
synchronized等待不可中断,ReentrantLock可以
可中断等待ReentrantLock可以让因为等待阻塞的线程不再继续等待
Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。乐观锁一般会使用版本号机制或CAS算法实现。