进程:
进程是具有一定独立功能的程序关于某个数据集合上的一次运动.
线程:
线程是进程里面的一个实体,是CPU调度或分派的基本单位
进程与线程之间的区别:
总而言之,一个进程里面可以有多个线程,但是一个进程至少有一个线程
多线程存在的意义:
程序允许有多个线程并发,提供程序的运行的效率
线程的创建方式:
new Thread().start();
多线程的应用:
Sun公司为我们提供了Thread类来实现线程
通过new Thread()来创建一个线程对象.
定义一个类继承Thread类,子类就有了线程的功能
创建Thread类的子类的对象,也可以创建一个线程.
线程的启用:
Thread类定义了run()方法,用于启动当前的线程
在启用当前的线程的时候,虚拟机会自动调用于run()方法
线程的其他方法:
sleep:使线程进入到睡眠的状态,需要唤醒的时间,会抛异常
setDaemon:将当前的线程设置为一个后台的线程,但主线程结束,它也会跟着结束
Join:合并一个线程,会抛出异常-->合并进来,join被谁调用,谁就执行.
currentThread:获取当前运行的线程.
package com.javami.kudy.DemoThread;
public class ThreadTest {
public static void main(String[]args)
{
int num = 0;
MyThread mt = new MyThread();
//为当前的线程起名字
mt.setName("kudy-0");
mt.setDaemon(true); //设置为后台线程,但主线程执行完毕.后台线程也会跟着退出
mt.start();
//一个进程至少有一个主线程
while(true)
{
if(num++>10)
break;
try{mt.join();}catch(Exception e){e.printStackTrace();}
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"Main()");
}
}
}
class MyThread extends Thread
{
@Override
public void run()
{
while(true)
{
//获取当前线程
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+" run()");
}
}
}
创建线程的方式二:
实现Runnable接口
子类覆盖父类中的run()犯法
通过Thread类创建线程,并且实现了Runnable接口中的子类
对象作为参数传递给Thread类型类的构造函数
这样做有什么好处:
线程开了,并且只是处理一个对象.用到了组合模式
为什么需要用到同步代码块或者同步函数:
由于程序在执行的时候,会出现线程不安全的问题,所以我们需要用到同步函数或者同步代码块.
导致线程不安全的注意问题:
1.多个线程随机访问延时
2.线程随机性
同步代码块:
Synchronized(对象) //锁旗标 0开 1关
{
需要同步的代码;
}
同步代码可以解决安全的问题根本原因在于对象上面,该对象如同锁的功能.
同步函数:
在函数上面加上synchronized修饰即可.
Public synchronized void sale()
{
//同步代码块使用的锁是this这个锁
}
死锁:
发生在相互调用的情况下,一个同步函数里要用到和同步代码块相同的锁,同步代码块又去调用函数,就用到函数的锁,两者都会锁死,发生死锁的问题.
package com.javami.kudy.DemoThread;
public class TicketsSale {
/**
* @param args
* 多线程售票:同时开启4个线程售票
*
* 线程安全问题在理想的状态下,不容易出现,但一旦出现就是对软件的影响是非常之大
* Thread-1 -1
Thread-0 0
Thread-3 1
----↓
同步代码块
与同步函数:
*/
public static void main(String[] args) {
SaleThread st = new SaleThread();
//4个进程产生
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
}
}
class SaleThread implements Runnable
{
String lock = ""; //锁期标
private int tickets = 10; //10张车票
@Override
public void run()
{
while(true) // 0 1 2 3 等待我执行完毕别的线程才去抢
{
synchronized(lock)
{
if(tickets<1)
break;
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+" "+tickets--);
}
}
}
}
同步的特点:
1.同步需要两个或者两个以上的线程.
2.多个线程使用的是同一把锁
3.为满足这两个条件,不能称其为同步.
同步的弊端:
1.当线程相当多的时候,因为每个线程都会去判断同步上的锁.这是很消耗资源的,判断该锁是否被锁上.如果锁上.需要等锁上的开了才可以互相抢进去.
无形中可以减低了程序运行的效率.
package com.javami.kudy.DemoThread;
public class TicketsSale1 {
/*
* 使用同步函数与同步代码块来实现4个线程
*/
public static void main(String[]args)
{
SaleThread1 st = new SaleThread1();
new Thread(st).start();
new Thread(st).start();
st.lock = "ok";
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
new Thread(st).start();
new Thread(st).start();
}
}
class SaleThread1 implements Runnable
{
String lock = " ";
private int num = 100;
public void run()
{
if(lock.equals("ok"))
{
while(true)
{
sale();
}
}else
{
while(true)
{
synchronized (this) {
if(num<1)
return;
System.out.println(Thread.currentThread().getName()+"else"+num--);
}
}
}
}
private synchronized void sale() {
if(num<1)
return;
System.out.println(Thread.currentThread().getName()+"if"+num--);
}
}
线程间的通信:
两个线程之间可以相互的通信
一个线程可以通过wait方法等待,这时CPU会让给别程
通过进行等待的线程需要其他线程调用notify()方法唤醒.
package com.javami.kudy.DemoThread;
/*
* 通信必须要在同步函数里面去解决的
*/
class SQL
{
public String name;
public String sex;
public boolean b = false;
}
class DBA implements Runnable
{
SQL sql ;
public DBA(SQL sql)
{
this.sql = sql;
}
public void run()
{
int num = 0;
while(true)
{
synchronized (sql) {
if(sql.b) //为真.我等待,让给别的进程~
try{sql.wait();}catch (Exception e) {e.printStackTrace();}
if(num ==0)
{
sql.name = "小细";
sql.sex = "男";
}else
{
sql.name = "美怡";
sql.sex = "女";
}
num = (num+1)%2; //实现了来回打印
sql.b = true;
sql.notify(); //等待完毕需要把你唤醒~
}
}
}
}
/*
* 编码员
*/
class Coder implements Runnable
{
SQL sql ;
public Coder(SQL sql)
{
this.sql = sql;
}
public void run()
{
while(true)
{
synchronized (sql) {
if(!sql.b) //如果为假-->我等待
try{sql.wait();}catch(Exception e){e.printStackTrace();}
System.out.print(sql.name+" ");
System.out.println(sql.sex+" ");
sql.b = false; //标记为假~~并且把这个进程唤醒
sql.notify();
}
}
}
}
public class SqlThread {
/**
* 模仿数据库的操作,实现边读取,边打印
*/
public static void main(String[] args) {
SQL sql = new SQL();
DBA dba = new DBA(sql);
Coder cr = new Coder(sql);
new Thread(dba).start(); //一个线程交换位置
new Thread(cr).start(); //另外一个线程马上就打印出来
}
}
思考:
wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
wait(),sleep()有什么区别?
Wait():假设我同步代码块抢到啦.我执行wait()方法,就会让该线程等待.让别的线程执行.
Notiyf():唤醒等待中的线程-->下一个
notiyfyAll(): 唤醒在此对象监视器上等待的所有线程。
Sleep():这个是去睡觉去啦~~~不需要你唤醒的,这家伙是自然醒.
使用1.5的lock和condition解决存和取之间的通信问题
package com.javami.kudy.DemoThread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 锁必须要一样,才能称得上同步
*/
class MyArray1
{
private int[] arr = new int[10];
private int savePos = 0;
private int getPos = 0;
private Lock lock = new ReentrantLock(); //创建一个锁
private int count = 0;
private Condition isFull = lock.newCondition(); //返回一个通信对象
private Condition isEmpty = lock.newCondition();//返回的地址一样
public void add(int num) throws InterruptedException
{
//但我执行慢的时候.我这个锁会开.开的时候.一看count==10等待..又抢,如果是你.你就等待.否则就是get 但是执行完毕一定要把当前的线程唤醒
try
{
lock.lock();//开锁
while(count==10)
isFull.await();//if等于10了,你就必须等待
if(savePos==10)
savePos = 0;
arr[savePos++] = num;
count++;
}finally
{
isEmpty.signal();//唤醒
lock.unlock();
}
}
public int get() throws InterruptedException
{
try
{
lock.lock(); //开锁
while(count==0)
isEmpty.await();//等待
if(getPos==10)
getPos = 0;
count--; //不-- ~? 数组问题
return arr[getPos++];
}finally
{
isFull.signal();//等待的哥们.我去叫醒你啦~~
lock.unlock();//开锁
}
}
}
public class ArrayThreadDemo {
static int num = 1;
public static void main(String[]args)
{
final MyArray1 ma = new MyArray1();
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0; i<30; i++)
{
try {
ma.add(num++);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0; i<30; i++)
{
try {
System.out.println(ma.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0; i<30; i++)
{
try {
ma.add(num++);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0; i<30; i++)
{
try {
System.out.println(ma.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
今天的知识点回顾:
总体来说:但是今天的精神很好..最后面的一道题目要多加练习
第二: 同步函数是:必须执行完才执行到你,但是同步完毕后不保证以后线程(抢)线程的情况.所以我们又用到 wait() 等待 唤醒(下一个的概念)
但是前提是同步函数里面使用.并且是同一把锁.注意锁的概念.程序要多敲-->后面理解毕竟不是很好.