//线程对象
class Demo extends Thread{
public void run(){
for(int x=0; x<60; x++)
System.out.println("demo run----"+x);
}
}
class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();//创建好一个线程。
//d.start();//开启线程并执行该线程的run方法。
d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
for(int x=0; x<60; x++)
System.out.println("Hello World!--"+x);
}
}
2、 实现方式
使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并复习其中run方法的方式。
创建步骤:
a,定义类实现Runnable的接口。
b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。
c,通过Thread类创建线程对象。
d,将Runnable接口的子类对象作为实参传递给Thread类的构造方法。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。
e,调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
程序示例:
public class RunnableDemo {
public static void main(String[] args) {
RDemo R = new RDemo();
Thread R1 = new Thread(R);// 创建了一个线程;
Thread R2 = new Thread(R);// 创建了一个线程;
R1.start();
R2.start();
}
}
class RDemo implements Runnable{
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName());
}
}
}
四、线程安全问题
1、导致安全问题的出现的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性 。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
a、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
示例:
//多线程售票案例
class Ticket implements Runnable{
private int tick = 1000;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){ //同步代码块,保证线程安全
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();
}
}
3、同步的前提
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
/*
加同步的单例设计模式————懒汉式
*/
class Single
{
private static Single s = null;
private Single(){}
public static void getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
//一个死锁的例子
package cn.thread;
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock dl1 = new DeadLock();
DeadLock dl2 = new DeadLock();
Thread dlt1 = new Thread(dl1);
Thread dlt2 = new Thread(dl2);
dl1.flag = 1;
dl2.flag = 2;
dlt1.start();
dlt2.start();
}
}
class DeadLock implements Runnable {
int flag = 1;
static String str1 = new String("1");
static String str2 = new String("2");
@Override
public void run() {
if (flag == 1) {
while (true) {
synchronized (str1) {
System.out.println("AAAA--11111111");
synchronized (str2) {
System.out.println("AAAA--22222222");
}
}
}
} else if (flag == 2) {
while (true) {
synchronized (str2) {
System.out.println("BBBB--11111111");
synchronized (str1) {
System.out.println("BBBB--22222222");
}
}
}
}
}
}
/**
* 线程通讯
* 消费者和生产者代码演示
* @author Administrator
*
*/
public class ProducerConsumerDemo3 {
public static void main(String[] args) {
Resource r=new Resource();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
/*
*生产者
*/
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r=r;
}
int bFull=0; //判断是否有生产的商品标记
@Override
public void run() {
while(true){
r.put();
}
}
}
/*
* 消费者
*/
class Consumer implements Runnable{
private Resource r;
Consumer(Resource r){
this.r=r;
}
@Override
public void run() {
while(true){
r.get();
}
}
}
/*
* 资源类
*/
class Resource{
private int count=0;
boolean bFull=false;
//生产商品
public synchronized void put() {
//如果商品存在,线程等待
while(bFull){ //while判断标记,让被唤醒的线程再一次判断标记。
try { wait();} catch (InterruptedException e) {}
}
//生产产品,将bFull标记为true,并唤醒所有的线程
System.out.println(Thread.currentThread().getName()+"....生产者....+商品+"+ ++count);
bFull=true;
notifyAll();
}
public synchronized void get() {
while(! bFull){
try {wait();} catch (Exception e) {}
}
//消费产品,将bFull标记为false,并唤醒所有的线程
System.out.println(Thread.currentThread().getName()+"。。。。。消费者。。。。。+商品+"+ count);
bFull=false;
notifyAll();
}
}
JDK1.5 中提供了多线程升级解决方案,涉及类Lock,Condition。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以Lock锁 进行获取。
Lock:替代了Synchronized
lock();
unlock();
newCondition()
Synchronized操作替换操作:
Lock l = ...;
l.lock(); // 上锁
try {
//同步代码块
} finally {
l.unlock(); //解锁
}
Condition:替代了Object 中wait(); notify(); notifyAll();
wait() ——> await();
notify() ——> signal();
notifyAll() ——> signalAll();
/* 资源类,JDK1.5 中提供了多线程升级解决方案,Lock,Condition。
*/
class Resource{
//定义锁对象
final Lock lock=new ReentrantLock();
final Condition condition_pro=lock.newCondition();
final Condition condition_con=lock.newCondition();
private int count=0;
boolean bFull=false;
//生产商品
public void put() {
lock.lock();
try {
//如果商品存在,线程等待
while(bFull){ //while判断标记,让被唤醒的线程再一次判断标记。
try { condition_pro.await();} catch (InterruptedException e) {}
}
//生产产品,将bFull标记为true,并唤醒所有的线程
System.out.println(Thread.currentThread().getName()+"....生产者....+商品+"+ ++count);
bFull=true;
//唤醒对方的一个线程
condition_con.signal();
}finally{
lock.unlock(); //释放锁
}
}
public void get() {
lock.lock();
try {
while(! bFull){
try {condition_con.await();} catch (Exception e) {}
}
//消费产品,将bFull标记为false,并唤醒所有的线程
System.out.println(Thread.currentThread().getName()+"。。。。。消费者。。。。。+商品+"+ count);
bFull=false;
//唤醒对方的一个线程
condition_pro.signal();
}finally{
lock.unlock(); //释放锁
}
}
}
(五)线程的停止
class StopThread implements Runnable {
private boolean flag = true;
public void run() {
while (flag) {
try {
wait();
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "....run");
flag=false;
}
}
}
// 标志Flag为false
public void changeFlag() {
flag = false;
}
}
class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
// st.changeFlag();
// t1.interrupt();
// t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "......."+ num);
}
System.out.println("over");
}
}