黑马程序员---java基础------------------多线程
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。
线程的创建
一:继承Thread类。步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。 目的:将自定义代码存储在run方法。让线程运行。
class Demo extends Thread
{
public void run()
{
for(int x=0; x<60; x++)
System.out.println("demo run----"+x);
}
}
3,调用线程的start方法, 该方法两个作用:启动线程,调用run方法。
class ThreadDemo {
public static void main(String[] args) {
//for(int x=0; x<4000; x++)
//System.out.println("Hello World!");
Demo d = new Demo();//创建好一个线程。
//d.start();//开启线程并执行该线程的run方法。
d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
for(int x=0; x<60; x++)
System.out.println("Hello World!--"+x);
}
}
线程的运行状态:
二:创建线程的第二种方式:实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。 将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
class Ticket implements Runnable//extends Thread {
private int tick = 100;
public void run() {
while(true) {
if(tick>0)
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
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);//创建了一个线程;
Thread t4 = new Thread(t);//创建了一个线程;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
获取当前线程对象:static Thread currentThread();
获取线程名称:getName();
class Test extends Thread {
Test(String name) { //父类有这样的构造方法
super(name);
}
public void run() {
for(int x=0; x<60; x++) {
System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one---");
Test t2 = new Test("two+++");
t1.start();
t2.start();
}
}
多线程的安全问题:通过分析,发现,打印出0,-1,-2等错票。问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执
行完,另一个线程参与进来执行。导致共享数据的错误。解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。这就出来了
同步代码块
synchronized(对象) {
需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中执行。 没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:1,必须要有两个或者两个以上的线程;2,必须是多个线程使用同一个锁。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
class Ticket implements Runnable {
private int tick = 1000;
Object obj = new Object();
public void run() {
while(true) {
synchronized(obj) {
if(tick>0) {
//try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
class TicketDemo2 {
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);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步函数:同步代码块用来封装代码的,函数也是来封装代码的,我们试图让函数具备同步性,也可以达到相同的效果。
class Bank {
private int sum;
public synchronized void add(int n) {
sum = sum + n;
try{Thread.sleep(10);
}catch(Exception 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();
}
}
验证同步函数的锁对象是this:如果直接将synchronized关键字放到run() 函数上做成同步函数,发现效果不行 ,应为只启动了0号线程,分析后得知,是同步的代码没弄清楚,后来将同步代码部分提取后,封装成独立的show()函数调用并同步synchronized,就可以了。这个时候问一个问题,它的锁是什么呢?函数需要被对象调用,函数都有一个所属对象的引用时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{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public synchronized void show()//this
{
if(tick>0) {
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().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);
t1.start();
try{
Thread.sleep(10);
}catch(Exception e){}
t.flag = false;
t2.start();
}
}
函数被静态修饰后,使用的锁是:类名.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(Ticket.class) {
if(tick>0) {
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show() {
if(tick>0) {
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().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);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
单例设计模式
懒汉式:
class Single {
private static Single s = null;
private Single(){}
public static Single getInstance() {
if(s==null) {
synchronized(Single.class) {
if(s==null)
//--->A;
s = new Single();
}
}
return s;
}
}
class SingleDemo {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
死锁:
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 {
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));
t1.start();
t2.start();
}
}