一、线程的创建
1.继承Thread类创建线程类
2.实现Runnable接口创建线程类
比较:
(1).使用Runnable接口时多个线程可以共享同一个target对象,所以非常适合多个线程来处理同一分资源的情况。
使用继承Tread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。
(2).继承Tread类不能再继承其他父类,实现接口类的方式还可以继承其他父类。
一般推荐使用实现Runnable接口的方式。、
二、线程的同步
线程会出现安全的问题。原因:多个线程在操作共享的数据
解决思路:就是将多条操作共享数据的线程代码封装起来,当有线程执行这些代码的时候其他线程不能参与进来
必须等当前线程把这些代码执行完了之后才允许其他线程进来参与运算。
同步的好处:解决线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁,消耗内存。
同步的前提:同步中必须有多个线程并使用同一个锁。
1.同步代码块
在Java中使用同步代码块可以解决安全问题。格式:synchronized(对象)
{封装代码块}
package com.company;
class Ticket extends Thread //implements Runnable
{ Object obj=new Object();
private int num=100; //将num设为静态的时候则是共享的数据那么这一百张票由几个线程共同卖完,
//如果使用实现接口方式那么也会由几个线程共享数据
public void run()
{
while(true)
{
synchronized (obj)
{
if (num > 0)
{
try { //在这里只能使用try catch。不能使用throw,因为在run方法中覆盖的Runnale,
// 而Runnable没有抛出,那么子类就不能抛出。
Thread.sleep(5); //sleep会抛出异常.
}
catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + "..sale.." + num--);
}
}
}
}
}
public class TicketDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();
/* Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
t1.start();
t2.start();
t3.start();*/
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
t1.start();
t2.start();
t3.start();
}
}
在这个代码案例中,在继承方式下当我将num值改为静态数据的时候,却依然出现了同步错误。
。。。。。。。。
Thread-1..sale..1
Thread-3..sale..0
Thread-2..sale..-1
对静态变量加锁要使用静态,静态锁需要一个用于整个类对象的锁,这个对象就是这个类(XXX.class)。
线程执行到代码块就获取到了锁!在这个线程还没有执行完的时候,如果其他线程也执行到这段代码块会检查这个对象的锁是否被其他线程取走,如果被取走,这个线程就阻塞等待
实现了线程互斥。所以检查到没有静态锁的时候,不能对num静态变量做同步安全措施。将锁obj改为Ticket.class即可。
2.在单例模式中加线程同步保护:
//饿汉式 多实际用
/*class Single
{
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
*/
//懒汉式 多面试
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if (s == null) //多加一次if判断是为了提高效率,实际上是将if判断提前了,于是少了锁的判断。
{
synchronized (Single.class) //必须用类名.class.因为在静态当中。
{
if (s == null)
s = new Single();
}
}
return s;
}
}
3.同步函数
* 同步函数使用的锁是this
*
* 同步函数和同步代码块的区别:
* 同步函数的锁是固定的this
* 同步代码块的锁是任意的对象
* 建议使用同步代码块。
*
* 在此代码块中,当同步函数和同步代码块持有相同的锁不会出现安全问题。
* 持不同的锁确相当于没有锁,会出现安全问题。
*
*静态的同步函数使用的锁是 该函数所属字节码文件对象 Class clazz=t.getClass(); Class clazz=Ticket.class
* 可以用getClass方法获取,也可以用当前 类名.class
下例分别使用静态同步代码块锁和静态同步函数锁。
class Tickets implements Runnable //extends Thread
{ Object obj=new Object();
private static int num=200; //将num设为静态的时候则是共享的数据那么这一百张票由几个线程共同卖完
boolean flag=true;
public void run()
{
if (flag) {
while (true) {
// synchronized (obj)
// {
show();
// }
}
} else
{
while(true)
{
synchronized (this.getClass())//Ticket.class
{if (num > 0)
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + "..obj.." + num--);
}
}
}
}
}
public static synchronized void show() //当使用静态函数时,不能this引用
{
if (num > 0)
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + "..static.." + num--);
}
}
}
public class SynFunctionLock
{ public static void main(String[] args)
{
Tickets t=new Tickets();
// Ticket tt=new Ticket();// 建立两个实例对象那么将会有两个num的实例值分别卖完.
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try {Thread.sleep(20);} catch (InterruptedException e) {}
t.flag=false;
t2.start();
}
}
4.死锁
死锁:常见场景之一,同步的嵌套
当两个线程互持对方的同步锁时就会发生死锁,程序不会给出异常,所有线程处于阻塞状态,无法继续。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
synchronized (MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"if locka");
synchronized (MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"if lockb");
}
}
}
else
{ synchronized (MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"else locka");
synchronized (MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"else locka");
}
}
}
}
}
class MyLock
{
public static final Object locka=new Object();
public static final Object lockb=new Object();
}
class DeadLockDemo
{
public static void main(String [] args)
{
Test a=new Test(true);
Test b=new Test(false);
Thread t1=new Thread(a);
Thread t2=new Thread(b);
t1.start();
t2.start();
}
}