------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
多线程:
线程和进程
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
JAVA VM 启动的时候会有一个进程java.exe。
该进程中至少有一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止衣蛾线程,还有负责垃圾回收机制的线程、
创建线程的方式:
创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread;
2,复写Thread类中的run 方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的方法,
该方法有两个左右:启动线程,调用run方法。
为什么要覆盖run方法呢?
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
示例:
package com.itheima;
public class ThreadDemo {
public static void main(String[] args) {
创建好一个线程。开启线程并执行该线程的run方法。
new Demo().start();
new Demo().start();
// new Demo().run();仅仅是对象调用方法。而线程创建了。并没有运行。
}
}
class Demo extends Thread{
@Override
public void run() {
// TODO Auto-generatedmethod stub
for (int i = 0; i < 60; i++) {
System.out.println("i="+i);
}
}
}
创建线程的第二种方式:实现Runable接口
步骤
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在run方法中
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为事件参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
示例:
package com.itheima;
public class RunnableDemo {
public static void main(String[] args) {
//创建了一个线程.
new Thread(new Demo2()).start();
new Thread(new Demo2()).start();
}
}
class Demo2 implements Runnable{
@Override
public void run() {
// TODO Auto-generatedmethod stub
for(int x = 0 ; x<60;x++){
System.out.println("x="+x);
}
}
}
实现方式和继承方式有什么区别?
实现方法的好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
两种方式的区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口子类的run方法。
线程运行状态:(自己画得,有点丑,见谅)
线程的安全问题
导致线程安全问题出现的原因:
多个线程访问出现延迟
线程的随机性。
线程安全在理想状态下不容易出现,但是一旦出现对元件的影响是巨大的。
解决方法:加入同步代码块:
格式:
synchronized(对象){
需要同步的代码;
}
同步的特点:
同步的前提:
同步需要两个或者两个以上的线程。
多个线程使用的是同一个锁。
未满足这两个条件,不能称为同步
同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
示例:
未使用synchroniezd代码:(加入sleep语句加以测试。)
package com.itheima;
public class TicketDemo {
public static void main(String[] args) {
Tick t= new Tick();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class Tick implements Runnable{
private int tick = 100;
@Override
public void run() {
// TODO Auto-generatedmethod stub
while(true){
if(tick>0){
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
}
}
}
}发现在输出结果中有-1,-2等,这是由于当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。(java中提供了专业的解决方法,就是同步代码块)
使用同步代码块后:
package com.itheima;
public class TicketDemo2 {
public static void main(String[] args) {
Tick2 t= new Tick2();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class Tick2 implements Runnable{
private int tick = 100;
Object obj = new Object();
@Override
public void run() {
// TODO Auto-generatedmethod stub
while(true){
synchronized (this) {
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
}
}
}
}
}
*同步函数:
格式:在函数上加上sychroniezed修饰符即可。
publicstatic sychronized void xxx(Xxx x){}
那么同步函数用的是哪个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以,用的是this。同步函数被this修饰后使用的锁是什么呢?通过验证,不再是this,因为今天函数没有this,静态进内存,内存中没有本类对象,但是一定有该类对应的字节码文件à类名.class,该对象的类型是Class
死锁
死锁。
同步中嵌套同步。
示例:
package com.itheima;
public class DieLockDemo {
public static void main(String[] args) {
new Thread(new Ticket(true)).start();
new Thread(new Ticket(false)).start();
}
}
class Ticket implements Runnable{
private boolean flag;
public Ticket(boolean flag) {
super();
this.flag = flag;
}
@Override
public void run() {
// TODO Auto-generatedmethod stub
if(flag){
synchronized (MyLock.locka) {
System.out.println("iflock--a");
synchronized (MyLock.lockb) {
System.out.println("if lock--b");
}
}
}else{
synchronized (MyLock.lockb) {
System.out.println("elselock--b");
synchronized (MyLock.locka) {
System.out.println("else lock--a");
}
}
}
}
}
class MyLock{
static Object locka = new Object();
static Object lockb = new Object();
}
线程间的通讯:
其实就是多个线程操作同一资源。
但是操作不同。
等待唤醒机制(wait() notify())
wait();notify();notifyAll();
都使用在同步中,因为要持有监视器(锁)的线程操作。所以要使用在同步中,因为同步才有锁。
为什么这些操作线程时要定义在Object类中呢?
因为这些方法在操作同步中的线程时都必须要标识他们所操作线程只有锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
即锁可以是任意对象的,所以可以被任意对象调用的方法定义在Object类中。
package com.itheima;
public class InputOutputDemo {
public static void main(String[] args) {
Res r= new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
class Res{
String name;
String sex;
boolean flag = false;
public synchronized void set(String name,String sex){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
this.name=name;
this.sex=sex;
flag = true;
this.notify();
}
public synchronized void out(){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
System.out.println(name+".."+sex);
flag=true;
this.notify();
}
}
class Input implements Runnable{
private Res r;
int x = 0;
public Input(Res r) {
super();
this.r = r;
}
@Override
public void run() {
// TODO Auto-generatedmethod stub
while(true){
if(x==0){
r.set("mike", "man");
}else{
r.set("丽丽", "女");
}
x=(x+1)%2;
}
}
}
class Outputimplements Runnable{
private Res r;
public Output(Res r) {
super();
this.r = r;
}
@Override
public void run() {
// TODO Auto-generatedmethod stub
while(true){
r.out();
}
}
}
同步1.5升级(Lock)
Lock对象的建立:
Lock lock = new ReentranLock();
Lock.lock();
Lock.unlock();
Object替代:Condition
await(); single(); singleAll();
Condition contidion = lock.newCondition();
该对象可以和Lock进行获取。
示例:
package com.itheima;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
public static void main(String[] args) {
LockTest lt = new LockTest();
new Thread(lt).start();
new Thread(lt).start();
new Thread(lt).start();
}
}
class LockTest implements Runnable{
private int x= 50;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generatedmethod stub
while(true){
lock.lock();
if(x>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+x--);
}
lock.unlock();
}
}
}