1.线程状态
线程共有有五种状态:新建;就绪;阻塞;运行;死亡
新建状态:通过new Thread()方法创建的新线程状态
就绪状态:通过start()方法启动之后进入就绪状态,并不立即执行,等待CPU调度
阻塞状态:通过sleep()方法或者同步锁使线程自运行状态转换为阻塞状态
运行状态:线程获得CPU调度开始执行
死亡状态:线程执行完毕进入死亡状态
状态转换图为:
join()方法同样可以使线程从运行进入阻塞状态
public class Text {
public static void main(String[] args) throws InterruptedException {
RunnableDome runnableDome=new RunnableDome();
//创建实现类的对象
Thread thread=new Thread(runnableDome);
thread.join();
//将此线程加入当前线程,即main方法所执行的线程执行完毕之后,再执行thread线程
//将实现类的对象作为参数传入Thread对象
thread.start();
}
}
sleep()方法代码实现为:
public class TicketThread extends Thread {
static int number=10;
static Object obj=new Object();
@Override
public void run() {
while (true){
synchronized (obj){
try {
Thread.sleep(1000);
//使程序进入休眠状态,sleep()方法参数为毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number > 0) {
System.out.println(Thread.currentThread().getName()+"买到了票"+number);
number--;
}
else {
break;
}
}
}
}
2.线程安全
2.1synchronized 关键字(隐式的同步)
引发线程安全的原因主要有两点,一是对共享资源的竟用,二是多条线程共同操作共享资源;synchronized的出现就是为了满足解决此类问题的需求,在java中,sycchronized可以实现在同一时刻,只有一个线程可以执行某个方法或代码块。在此线程进入共享资源后,自动加锁,其余线程在外等待,执行完毕后,自动解锁。这个过程是一个隐式的过程。synchronized的三中应用方式分别为:
这里先给出买票的一个测试用例,三种应用方式的测试类相同代码实现为:
public class Text {
public static void main(String[] args) {
TicketThread ticketThread1= new TicketThread();
ticketThread1.setName("窗口1");
ticketThread1.start();
TicketThread ticketThread2=new TicketThread();
ticketThread2.setName("窗口2");
ticketThread2.start();
System.out.println(ticketThread1.getClass()==ticketThread2.getClass());
}
}
synchronized修饰代码块
指定加锁对象,在线程获取该对象的锁之后,其余线程无法获取,注意此时这个对象应当是唯一的
代码实现:
public class TicketThread extends Thread {
static int number=10;
static Object obj=new Object();
@Override
public void run() {
while (true){
//修饰代码块,且传入的对象(obj)是唯一的
synchronized (obj){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number > 0) {
System.out.println(Thread.currentThread().getName()+"买到了票"+number);
number--;
}
else {
break;
}
}
}
}
synchronized修饰实例方法
此时synchronized默认的锁标志即传入的对象为this,注意如果此时有多个线程同时访问时,是允许的,因为各自线程对象的锁时不同的,如若此时访问的是共享资源,那么线程安全则不能被保证
public class TicketThread extends Thread {
static int number=10;
static Object obj=new Object();
@Override
public void run() {
ticket();
}
/*
窗口1买到了票10
窗口2买到了票10
窗口1买到了票9
窗口2买到了票8
窗口1买到了票7
窗口2买到了票6
窗口1买到了票5
窗口2买到了票4
窗口1买到了票3
窗口2买到了票2
窗口1买到了票1
上述结果的出现就是因为,synchronized修饰成员方法时,锁标志默认的对象是this,每一个线程进去都会加上自己的一把锁,
这把锁对于其它线程是无效的,因此可能会出现第一个线程进去,还未做number--操作时时,第二个线程进去
*/
public synchronized void ticket() {
while(true){
if(number>0){
System.out.println(Thread.currentThread().getName()+"买到了票"+number);
number--;
}
else break;
}
}
}
synchronized修饰静态方法
注意此时与修饰成员方法不同的点是此时synchronized锁标志即传入的对象为:class对象,此时即使是多个线程访问这一共享资源,也是线程安全的;因为在同一个类中,多个线程的class对象是相同的,即所加的锁对所有线程都有效,System.out.println(ticketThread1.getClass()==ticketThread2.getClass());// 可用此语句判断两个线程对应的Class对象是否相同;
代码实现:
public class TicketThread extends Thread {
static int number=10;
static Object obj=new Object();
@Override
public void run() {
ticket();
}
/*
修饰静态方法时则不会出现此类情况,原因是修饰静态方法时,锁标志的对象默认为对应的Class对象,只要两个线程对象是
同一个类的对象,则对应的Class对象相同,System.out.println(ticketThread1.getClass()==ticketThread2.getClass());
可用此语句判断两个线程对应的Class对象是否相同
则这把锁对两个线程都有作用,当一个线程进去时,锁关闭,另一个线程则在阻塞状态。
*/
public synchronized void ticket() {
while(true){
if(number>0){
System.out.println(Thread.currentThread().getName()+"买到了票"+number);
number--;
}
else break;
}
}
}
2.2 Lock(显式的)
Lock主要是通过Lock接口的实现类ReentrantLock来实现线程安全;与synchronized关键字的不同是需要手动关闭与释放锁,加锁的方法为lock();释放的方法为unlock();注意在发生异常时,不会自动释放锁,因此一般来说,使用Lock必须在try{}catch{}块中进行,且释放锁必须放在finally块中进行,确保所以定被释放,防治死锁的出现
代码实现为:
import javax.swing.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentantLockDome extends Thread {
static int number = 10;
static Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁,使代码快进入锁定状态,其余的线程排队等候
Thread.sleep(50);
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "买到了票" + number);
number--;
}else {
Thread.currentThread().stop();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
}
测试类为 :
import synchronzied.TicketThread;
public class Text {
public static void main(String[] args) {
ReentantLockDome r1=new ReentantLockDome();
ReentantLockDome r2=new ReentantLockDome();
r1.setName("窗口1");
r2.setName("窗口2");
r1.start();
r2.start();
}
}