一、进程与线程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整的过程,这个过程也是进错本身从产生、发展到最终消亡的过程。
多线程是实现并发机制的一种有效的手段。进错和线程一样,都是实现并发的一个基本单位。
实现多线程的2种方式:
1.继承Thread类
2.实现Runnable接口
import java.lang.*;
class MyThread extends Thread
{
private int ticket=5; //5张票要卖出去
public void run(){ //必须覆写run()方法,作为线程的操作主体
for(int i=0;i<100;i++){
if(this.ticket>0){
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
class MyRunnable implements Runnable
{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
if(this.ticket>0){
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
public class TreadDemo
{
public static void main(String[] args)throws Exception{
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
/*System.out.println("***MyThread线程1卖票***");
mt1.run();
System.out.println("***MyThread线程2卖票***");
mt2.run();
System.out.println("***MyThread线程3卖票***");
mt3.run();*/
mt1.run();
MyRunnable mr1=new MyRunnable();
System.out.println("***MyRunnable线程1卖票***");
new Thread(mr1).run();
new Thread(mr1).run();
new Thread(mr1).run();
}
}
继承Thread类:继承Thread类,此类就称为多线程操作类。在Thread子类之中,必须明确的覆写Thread类中的run()方法,此方法为线程的主体。
class MyThread extends Thread{
属性。。;
方法。。。;
public void run(){//覆写Thread类中的run()方法
线程主体;
}
}
public class Test{
public static void main(String[] args) throws Exception{
MyThread mt=new MyThread();
mt.start();
}
}
二、线程的使用和同步
同步很重要的;
问题:以卖火车票为例,如果现在是要想买火车票,可以在网上买或者各个售票点买,但是不管有多少个地方可以买,最终一趟列车的车票数量是固定的,如果把各个售票点理解成为各个线程的话,则所有的线程应该共同拥有同一份的票数。
如下面的代码:
class MyRunnable implements Runnable
{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
if(this.ticket>0){
try{
Thread.sleep(300);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
public class TreadDemo2
{
public static void main(String[] args)throws Exception{
MyRunnable mt=new MyRunnable();
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
//注:程序运行的结果会出现 ticket=-1的情况。
原因:分析:以上程序有 1.判断票数是否大于0,大于0则表示还有票可以卖。
2.如果票数大于0,则买票出去
但是,上面的操作中第1步和第二步之间加入了延迟操作,那么一个线程就有可能在还没有对票数进行减操作之前,其他线程就已经将票数减少了,这样依赖就会出现票数为负的情况。
解决方法:使用同步,所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
要想解决资源共享的同步操作问题,可以使用同步代码块及同步方法两中方式完成。
1.同步代码块(使用synchronized关键字声明的代码块)
格式:synchronized(同步对象){
需要同步的代码;
}
同步的时候必须指明同步的对象,一般情况下将当前对象作为同步的对象,使用this表示。
class MyRunnable implements Runnable
{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
synchronized(this){ //synchronied同步
if(this.ticket>0){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
}
public class TreadDemo2
{
public static void main(String[] args)throws Exception{
MyRunnable mt=new MyRunnable();
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
//注意:这种方法效率明显会降低。
2.同步方法
前面是同步代码块,也可以是用synchronized关键字将一个方法声明成同步方法。
格式:
synchronized 方法返回值 方法名称(参数列表){}
}
public class TreadDemo2
{
public static void main(String[] args)throws Exception{
MyRunnable mt=new MyRunnable();
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
四、多线程案例:生产者与消费者
问题:生产一个取走一个
package com.test;
import java.lang.*;
class Info{
private String name="初始姓名";
private String content="初始内容";
private boolean flag=false;
public synchronized void set(String name,String content) throws InterruptedException{
if(!flag){
super.wait();
}
this.setName(name);
Thread.sleep(300);
this.setContent(content);
flag=false;
super.notify();//唤醒线程
}
public synchronized void get() throws InterruptedException{
if(flag){
super.wait();
}
Thread.sleep(300);
System.out.println("姓名:"+this.getName()+'\t'+"内容:"+this.getContent());
flag=true; //改变flag,表示已经取走可以生产
super.notify();//唤醒
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
class Producer implements Runnable{
private Info info=null;
public Producer(Info info){
this.info=info;
}
public void run(){
boolean flag=false;//定义标记位
for(int i=0;i<10;i++){
if(flag){
try {
System.out.print("生成1="+i+'\t');
this.info.set("张三", "男的");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag=false;
}else{
try {
System.out.print("生成2="+i+'\t');
this.info.set("abcd", "abcdefg");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag=true;
}
}
}
}
class Consumer implements Runnable{
private Info info=null;
public Consumer(Info info){
this.info=info;
}
public void run(){
for(int i=0;i<10;i++){
try {
System.out.print("取走="+i+'\t');
this.info.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class ThreadTestDemo01 {
public static void main(String[] args){
Info info=new Info();
Producer pro=new Producer(info);
Consumer con=new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
注:上面的代码完成了生成一个取走一个。
细细研究的时候还请大家多看看书才行哦。