*08-多线程(线程的状态)
CPU的执行资格:可以被CPU处理但在处理队列排列
CPU的执行权:正在被CPU执行
*09-多线程(创建线程的第二种方式—实现Runnable接口)
类,类中的代码不止一个人要用。而且需要同时执行。这个时候就需要线程来实现。
package testThread;
public class main {
public static void main(String[] args) {
myThread t1 = new myThread();
myThread t2 = new myThread();
t1.start();
t2.start();
}
}
class myThread extends Thread{
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}
但假如myThread有父类,就不能再继承Thread了。那该怎么办?
当一个类有父类的时候就不能再继承Thread的了。
不继承也有其他方法解决。——扩展Demo类的内容,实现多功能,接口。implement
class Demo implements Runnable//extends Fu //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
//通过接口的形式完成。
{
public void run()
{
show();
}
public void show()
{
for(int x=0; x<20; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
// Demo d1 = new Demo();<span style="white-space:pre"> </span>d1不是线程对象
// Demo d2 = new Demo();
// d1.start();
// d2.start();
}
}
/*
创建线程的第一种方式:继承Thread类。
创建线程的第二种方式:实现Runnable接口。
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
4,调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。
所以,创建线程的第二种方式较为常用。
*/
*10-多线程(第二种方式的细节)
内部的设计思想
class Thread
{
private Runnable r;
Thread()
{
}
Thread(Runnable r)
{
this.r = r;
}
public void run()
{
if(r!=null)
r.run();
}
public void start()
{
run();
}
}
class ThreadImpl implements Runnable
{
public void run()
{
System.out.println("runnable run");
}
}
ThreadImpl i = new ThreadImpl();
Thread t = new Thread(i);
t.start();
class SubThread extends Thread
{
public void run()
{
System.out.println("hahah");
}
}
//SubThread s = new SubThread();
//s.start();
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。
所以,创建线程的第二种方式较为常用。
*12-多线程(卖票示例)
写程序的步骤:
1)写需求
2)。。。
。。。。
package testThread;
class Ticket implements Runnable {
private int num = 100;
@Override
public void run() {
while (num > 0) {
System.out
.println(Thread.currentThread().getName() + "..." + num--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
*13-多线程(线程安全问题的现象)
我们分析上面这个程序的时候可以发现,存在一些情况。
package testThread;
class Ticket implements Runnable {
private int num = 10;
@Override
public void run() {
while (num > 0) {
for(int i = 0;i<999999;i++){}
System.out
.println(Thread.currentThread().getName() + "..." + num--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行的结果当中出现了这样的情况:
Thread-1...10
Thread-2...9
Thread-0...8
Thread-2...7
Thread-1...6
Thread-3...5
Thread-2...4
Thread-1...3
Thread-2...2
Thread-3...1
Thread-2...0
Thread-1...-1
Thread-0...-2
或者用thread.sleep(10)来进行停顿,看效果。
如何避免呢?
*14-多线程(线程安全问题产生的原因)
线程安全问题产生的原因:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条
*15-多线程(同步代码块)
解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
class Ticket implements Runnable//extends Thread
{
private int num = 100;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
}
}
}
}
}
完整代码:
package testThread;
class Ticket implements Runnable {
private int num = 1000;
Object obj = new Object();
@Override
public void run() {
while (num > 0) {
synchronized (obj) {
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + "..."
+ num--);
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
*16-多线程(同步的好处和弊端)
加了synchronized 关键字之后,为什么就能保证其他线程进不来了呢?
同步的好处:解决了线程的安全问题。同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。效率低是在我们可承受范围之内。
同步的前提:同步中必须有多个线程并使用同一个锁。
*17-多线程(同步的前提)
同步的前提:同步中必须有多个线程并使用同一个锁。
*18-多线程(同步函数)
银行存款的例子。
/*
需求:储户,两个,每个都到银行存钱每次存100,,共存三次。
*/
class Bank
{
private int sum;
// private Object obj = new Object();
public synchronized void add(int num)//同步函数
{
// synchronized(obj)
// {
sum = sum + num;
// -->
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println("sum="+sum);
// }
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x=0; x<3; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
同步代码块——用的锁是我们自己建立的obj
同步函数——的锁是什么呢?
都可以保证同步。
---------------------------------------
package testThread;
class Bank {
private int sum;
public void add(int num) {
sum = num + sum;
System.out.println("-->sum:" + sum);
}
}
class Person implements Runnable {
Bank b = new Bank();
@Override
public void run() {
for (int i = 0; i < 3; i++) {
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Person p = new Person();
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
上面的代码会出现线程不安全的情况,结果的输出为:
-->sum:200
-->sum:200
-->sum:400
-->sum:500
-->sum:300
-->sum:600
方法1:
package testThread;
class Bank {
private int sum;
private Object obj = new Object();
public void add(int num) {
// 方法一,在下面的语句添加syncni关键字
synchronized (obj) {
sum = num + sum;
System.out.println("-->sum:" + sum);
}
}
}
class Person implements Runnable {
Bank b = new Bank();
@Override
public void run() {
for (int i = 0; i < 3; i++) {
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Person p = new Person();
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
方法2:
package testThread;
class Bank {
private int sum;
public synchronized void add(int num) {// 方法二,同步函数
sum = num + sum;
System.out.println("-->sum:" + sum);
}
}
class Person implements Runnable {
Bank b = new Bank();
@Override
public void run() {
for (int i = 0; i < 3; i++) {
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Person p = new Person();
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
*19-多线程(验证同步函数的锁)
验证同步代码块和同步函数使用的锁是不是同一个。
同步函数的锁是this。
同步函数的使用的锁是this;
同步函数和同步代码块的区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。
举例:
package testThread;
class Ticket implements Runnable {
private int num = 100;
boolean flag = true;
Object obj = new Object();
@Override
public void run() {
if (flag)
while (true) {
synchronized (obj) {
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "...obj..." + num--);
}
}
}
else
while (true) {
show();
}
}
public synchronized void show() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num > 0)
System.out.println(Thread.currentThread().getName() + "...func..."
+ num--);
}
}
public class TicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.flag = false;
t2.start();
}
}
上面的代码会有着两种结果输出:
①
②
结果就说明了两个不是同一个锁。
修改:
if (flag)
while (true) {
synchronized (this) {
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "...obj..." + num--);
}
}
}
else
while (true) {
show();
}
*20-多线程(验证静态同步函数的锁)
把num的值改为statci。
输出的结果不正确,说明了静态同步函数的锁不是this,原因很简单,因为静态函数没有this。
/*
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。
*/
class Ticket implements Runnable
{
private static int num = 100;
// Object obj = new Object();
boolean flag = true;
public void run()
{
// System.out.println("this:"+this.getClass());
if(flag)
while(true)
{
synchronized(Ticket.class)//(this.getClass())
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....obj...."+num--);
}
}
}
else
while(true)
this.show();
}
public static synchronized void show()
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....function...."+num--);
}
}
}
class StaticSynFunctionLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
// Class clazz = t.getClass();
//
// Class clazz = Ticket.class;
// System.out.println("t:"+t.getClass());
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag = false;
t2.start();
}
}
总结:
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。
*21-多线程(单例模式涉及的多线程问题)
/*
多线程下的单例
*/
//饿汉式
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(Single.class)
{
if(s==null)
// -->0 -->1
s = new Single();
}
}
return s;
}
}
在多线情况下,有没有安全隐患。
饿汉式没有,懒汉式有。
解决方式1;
但这样的话,就会降低效率。因为假如想取得一个实例,首先要判断锁,之后在判断是否为空。这个时候,我们可以对代码进行改写。
getClass是非静态的的。不能用做锁。
所以可以用这种双重判断来解决单例模式的效率和安全问题。
*22-多线程(死锁示例)
死锁的体现形式之一 —— 同步的嵌套。
什么是死锁,看下面的例子、
/*
死锁:常见情景之一:同步的嵌套。
*/
class Ticket implements Runnable
{
private int num = 100;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
while(true)
{
synchronized(obj)
{
show();
}
}
else
while(true)
this.show();
}
public synchronized void show()
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
// System.out.println("t:"+t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
t.flag = false;
t2.start();
}
}
面试:请写出一个死锁程序。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"..if locka....");
synchronized(MyLock.lockb) {
System.out.println(Thread.currentThread().getName()+"..if lockb....");
}
}
}
else
{
while(true)
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..else lockb....");
synchronized(MyLock.locka)
{
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 DeadLockTest
{
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();
}
}
*郁闷答疑——关于Obj放的位置