一、多线程-同步函数的锁是this
/*
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。
通过该程序进行验证。
使用两个线程来买票。
一个线程在同步代码块中。 这两个的锁不一样
一个线程在同步函数中。
都在执行买票动作。
*/
class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(this) /*同步函数的锁*/
{
if(tick>0)
{
try{(10);}catch(Exception e){}
(().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show(); /*使用this锁*/
}
public synchronized void show()//this
{
if(tick>0)
{
try{(10);}catch(Exception e){}
(().getName()+"....show.... : "+ tick--);
}
}
}
class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
();
try{(10);}catch(Exception e){}
= false;
();
// Thread t3 = new Thread(t);
// Thread t4 = new Thread(t);
// ();
// ();
}
}
二、多线程-静态同步函数的锁是class对象
/*
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
*/
class Ticket implements Runnable
{
private static int tick = 100;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized() /*使用了静态,锁就变了*/
{
if(tick>0)
{
try{(10);}catch(Exception e){}
(().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{(10);}catch(Exception e){}
(().getName()+"....show.... : "+ tick--);
}
}
}
class StaticMethodDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
();
try{(10);}catch(Exception e){}
= false;
();
}
}
三、多线程-单例设计模式-懒汉式 出现的安全问题
/*
单例设计模式。 必须要记住,面试题
饿汉式与懒汉式的区别
1.懒汉式是延时加载的形式
2.懒汉式由于是延时加载,所以在多线程中会出现安全问题
3.可以加同步函数或者同步代码块来解决,而同步函数每次都需要判断比较慢
4.这里采用同步代码块来解决安全问题
5.由于函数是静态的,所以锁是类名.class
*/
//饿汉式。
/*
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)
{
synchronized()
{
if(s==null)
//--->A;
s = new Single();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
("Hello World!");
}
}
四、死锁(要避免出现和熟悉)
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
= flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized()
{
(().getName()+"...if locka ");
synchronized()
{
(().getName()+"..if lockb");
}
}
}
}
else
{
while(true)
{
synchronized()
{
(().getName()+"..else lockb");
synchronized()
{
(().getName()+".....else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka = new Object(); /*静态,能用类名直接调用*/
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
();
();
}
}
多线程(面试中的线程问题)
线程状态:
1、新建状态:当用new操作符创建一个线程时,线程还没有开始运行线程中的代码,此时线程处在新建状态。
2、就绪状态:当线程对象调用start()方法即启动了线程,等待调度程序(thread scheduler)分配CPU时间的状态。
3运行状态(Running): 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4.阻塞状态(Blocked):所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
进入阻塞状态的原因:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;
......
5.死亡状态(Dead): 1) run方法正常退出而自然死亡 2) 一个未捕获的异常终止了run方法而使线程猝死。
Java中创建线程主要有三种方式:1.继承Thread类2.实现Runnable接口3.创建Callable接口的实现类(有返回值)
一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程
要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体
同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对
象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行
体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask
对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
线程终止的三种情况:
1 线程正常执行结束。
2 线程抛出异常且未被捕获。
3 线程调用stop()方法终止线程,该方法已经过期。不建议使用
线程方法:
sleep():让线程进入睡眠等待一段时间,线程进入阻塞状态,时间过后进入就绪状态。
interrupt():让当前线程终止等待,进入就绪状态。
Join():让当前线程等待加入的线程完成后才能继续执行
setPriority():设置线程优先级,最高10,最低1,默认5.
setDaemon(true):守护线程必须在调用start()方法之前,设置守护线程,当程序中只有守护程序时,该程序运行结束。
线程锁关键字:synchronized (同步)
定义锁对象:staticfinalObjectlock=newObject();
对lock对象上锁: synchronized(lock);
对方法上锁:privatestaticsynchronizedvoiddoTask(inttid)
解决线程死锁:多个线程上锁的顺序要一致。
线程协作:
void wait():当前线程等待,等待其他线程调用对象的notify()或notifyAll()方法
void notify():唤醒在此对象锁上等待的单个线程
void notifyAll():唤醒在此对象锁上等待的所有线程
线程池的作用:
1.提高程序的执行效率
2.控制线程的数量,防止程序崩溃
创建线程池的常用方法:
ForkJoinPool:作用充分挖掘利用多核CPU的能力,把一个大任务分解成多个小任务,执行完后再行合并。
线程池的使用方法:
1、创建一个线程池
2、创建一个Runnable对象
3、将Runnable对象作为线程任务提交到线程池中,调用线程池的submit或excute方法
4、使用完线程池后,调用线程的shutdown()方法关闭线程池(不会立马关闭 先执行完线程池中的所有已提交任务后 依次关闭线程对象 所有的线程对象进入死亡状态)
注:shutdownNow();//试图停止正在执行的线程任务,暂停正在等待的任务..但这是试图,也不一定立马关闭。
线程池的submit和excute方法的区别:
1、接收的参数不一样
2、submit有返回值,而execute没有
3、submit方便Exception处理